Zabezpiecz swoje API Django REST Framework za pomocą solidnego uwierzytelniania. Porównaj uwierzytelnianie Token i implementację JWT (JSON Web Token), w tym praktyczne przykłady kodu i najlepsze praktyki.
Uwierzytelnianie Python DRF: Implementacja Token vs. JWT dla solidnych API
Zabezpieczenie Twoich API jest najważniejsze. Podczas budowania API za pomocą Pythona i Django REST Framework (DRF), masz do dyspozycji kilka opcji uwierzytelniania. Ten artykuł zagłębia się w dwie popularne metody: uwierzytelnianie Tokenem i uwierzytelnianie JWT (JSON Web Token), porównując ich mocne i słabe strony oraz przedstawiając praktyczne przykłady implementacji.
Zrozumienie uwierzytelniania w API
Uwierzytelnianie to proces weryfikacji tożsamości użytkownika lub aplikacji uzyskującej dostęp do Twojego API. Dobrze zaimplementowany system uwierzytelniania zapewnia, że tylko autoryzowane podmioty mogą uzyskiwać dostęp do chronionych zasobów. W kontekście API RESTful, uwierzytelnianie zazwyczaj obejmuje wysyłanie danych uwierzytelniających (np. nazwy użytkownika i hasła) z każdym żądaniem. Serwer następnie weryfikuje te dane uwierzytelniające i, jeśli są one prawidłowe, przyznaje dostęp.
Uwierzytelnianie Tokenem
Uwierzytelnianie Tokenem to prosty i bezpośredni mechanizm. Gdy użytkownik pomyślnie się zaloguje, serwer generuje unikalny, losowy token i przechowuje go w bazie danych, kojarząc go z użytkownikiem. Klient następnie wysyła ten token w nagłówku 'Authorization' kolejnych żądań. Serwer pobiera token z bazy danych, weryfikuje jego ważność i odpowiednio przyznaje dostęp.
Implementacja z DRF
DRF zapewnia wbudowaną obsługę uwierzytelniania Tokenem. Oto jak to zaimplementować:
- Zainstaluj DRF i zarejestruj go w swoim projekcie Django:
Najpierw upewnij się, że masz zainstalowany Django REST Framework:
pip install djangorestframework
Następnie dodaj go do `INSTALLED_APPS` w `settings.py`:
INSTALLED_APPS = [
...
'rest_framework',
]
- Dodaj schemat TokenAuthentication jako domyślną klasę uwierzytelniania (opcjonalne, ale zalecane):
W swoim pliku `settings.py` dodaj następujące elementy:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}
Spowoduje to zastosowanie uwierzytelniania Tokenem globalnie w całym Twoim API. `SessionAuthentication` jest zawarte dla interakcji opartej na przeglądarce, ale możesz je usunąć dla aplikacji opartej wyłącznie na API.
- Utwórz Token dla każdego użytkownika:
Możesz automatycznie tworzyć tokeny dla użytkowników podczas tworzenia, dodając obsługę sygnałów. Utwórz plik o nazwie `signals.py` w swojej aplikacji (np. `users/signals.py`):
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
Następnie zaimportuj ten plik `signals.py` w swoim pliku `users/apps.py` w metodzie `ready` klasy konfiguracyjnej aplikacji. Przykład dla `users/apps.py`:
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.BigAutoField'
name = 'users'
def ready(self):
import users.signals
Teraz możesz zarządzać tokenami za pomocą wiersza poleceń:
python manage.py drf_create_token <username>
- Zaimplementuj swoje widoki API:
Oto prosty przykład widoku, który wymaga uwierzytelniania Tokenem:
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
content = {
'message': 'Hello, ' + request.user.username + '! You are authenticated.',
}
return Response(content)
W tym przykładzie `authentication_classes` określa, że powinno być używane uwierzytelnianie Tokenem, a `permission_classes` określa, że tylko uwierzytelnieni użytkownicy mogą uzyskać dostęp do widoku.
- Dołącz widok API logowania:
Potrzebujesz również punktu końcowego do tworzenia tokena po pomyślnym zalogowaniu:
from django.contrib.auth import authenticate
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user:
token, _ = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
else:
return Response({'error': 'Invalid Credentials'}, status=status.HTTP_401_UNAUTHORIZED)
Zalety uwierzytelniania Tokenem
- Prostota: Łatwa implementacja i zrozumienie.
- Bezstanowość: Każde żądanie tokena zawiera informacje, które pozwalają mu istnieć samodzielnie.
Wady uwierzytelniania Tokenem
- Zależność od bazy danych: Wymaga wyszukiwania w bazie danych dla każdego żądania, aby zweryfikować token. Może to wpływać na wydajność, zwłaszcza w dużej skali.
- Odwoływanie tokenów: Odwołanie tokena wymaga usunięcia go z bazy danych, co może być skomplikowane.
- Skalowalność: Może nie być najbardziej skalowalnym rozwiązaniem dla dużych API o dużym natężeniu ruchu ze względu na obciążenie bazy danych.
Uwierzytelnianie JWT (JSON Web Token)
Uwierzytelnianie JWT to nowocześniejsze i bardziej wyrafinowane podejście. JWT to kompaktowy, bezpieczny dla adresów URL obiekt JSON, który zawiera informacje o użytkowniku. Te informacje są podpisywane cyfrowo za pomocą klucza tajnego lub pary kluczy publiczny/prywatny. Gdy użytkownik się zaloguje, serwer generuje JWT i wysyła go do klienta. Klient następnie dołącza ten JWT w nagłówku 'Authorization' kolejnych żądań. Serwer może zweryfikować podpis JWT bez konieczności uzyskiwania dostępu do bazy danych, co czyni go bardziej wydajnym i skalowalnym rozwiązaniem.
Implementacja z DRF
DRF nie zapewnia wbudowanej obsługi uwierzytelniania JWT, ale kilka doskonałych bibliotek ułatwia integrację. Jedną z najpopularniejszych jest `djangorestframework-simplejwt`.
- Zainstaluj `djangorestframework-simplejwt`:
pip install djangorestframework-simplejwt
- Skonfiguruj ustawienia DRF:
W swoim pliku `settings.py` dodaj następujące elementy:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': settings.SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
}
Wyjaśnienie ustawień:
- `ACCESS_TOKEN_LIFETIME`: Jak długo ważny jest token dostępu (na przykład 5 minut).
- `REFRESH_TOKEN_LIFETIME`: Jak długo ważny jest token odświeżania (na przykład 1 dzień). Tokeny odświeżania służą do uzyskiwania nowych tokenów dostępu bez konieczności ponownego logowania się użytkownika.
- `ROTATE_REFRESH_TOKENS`: Czy obracać tokeny odświeżania po każdym użyciu.
- `BLACKLIST_AFTER_ROTATION`: Czy umieszczać na czarnej liście stare tokeny odświeżania po rotacji.
- `ALGORITHM`: Algorytm używany do podpisywania JWT (HS256 jest powszechnym wyborem).
- `SIGNING_KEY`: Klucz tajny używany do podpisywania JWT (zazwyczaj Twój Django SECRET_KEY).
- `AUTH_HEADER_TYPES`: Typ nagłówka autoryzacji (zazwyczaj "Bearer").
- Dołącz widoki API logowania i odświeżania tokena:
`djangorestframework-simplejwt` udostępnia widoki do uzyskiwania i odświeżania tokenów. Dołącz je do swojego `urls.py`:
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
`TokenObtainPairView` udostępnia tokeny dostępu i odświeżania po pomyślnym uwierzytelnieniu. `TokenRefreshView` udostępnia nowy token dostępu po udostępnieniu prawidłowego tokena odświeżania.
- Zaimplementuj swoje widoki API:
Oto prosty przykład widoku, który wymaga uwierzytelniania JWT:
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.authentication import JWTAuthentication
class ExampleView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
content = {
'message': 'Hello, ' + request.user.username + '! You are authenticated.',
}
return Response(content)
Podobnie jak w przykładzie uwierzytelniania Tokenem, `authentication_classes` określa, że powinno być używane uwierzytelnianie JWT, a `permission_classes` ogranicza dostęp tylko do uwierzytelnionych użytkowników.
Zalety uwierzytelniania JWT
- Skalowalność: Do walidacji tokena nie jest wymagane wyszukiwanie w bazie danych, co czyni go bardziej skalowalnym.
- Bezstanowość: JWT zawiera wszystkie niezbędne informacje do uwierzytelniania.
- Standaryzacja: JWT jest szeroko przyjętym standardem, obsługiwanym przez wiele bibliotek i platform.
- Przyjazny dla mikroserwisów: Odpowiedni dla architektur mikroserwisów, ponieważ usługi mogą niezależnie weryfikować JWT.
Wady uwierzytelniania JWT
- Złożoność: Bardziej złożony w implementacji niż uwierzytelnianie Tokenem.
- Rozmiar tokena: JWT mogą być większe niż proste tokeny, co potencjalnie zwiększa wykorzystanie przepustowości.
- Odwoływanie tokenów: Odwołanie JWT jest trudne. Po wydaniu jest ważny do czasu jego wygaśnięcia. Obejścia obejmują umieszczanie odwołanych tokenów na czarnej liście, co ponownie wprowadza zależność od bazy danych.
Strategie odwoływania tokenów
Zarówno metody uwierzytelniania Tokenem, jak i JWT wymagają mechanizmów odwoływania dostępu. Oto jak możesz podejść do odwoływania tokenów:
Odwoływanie uwierzytelniania tokenem
W przypadku uwierzytelniania Tokenem odwołanie jest proste: po prostu usuń token z bazy danych:
from rest_framework.authtoken.models import Token
try:
token = Token.objects.get(user=request.user)
token.delete()
except Token.DoesNotExist:
pass
Odwoływanie uwierzytelniania JWT
Odwołanie JWT jest bardziej złożone, ponieważ sam token jest samodzielny i nie polega na wyszukiwaniu w bazie danych w celu walidacji (początkowo). Typowe strategie obejmują:
- Czarna lista tokenów: Przechowuj odwołane tokeny na czarnej liście (np. w tabeli bazy danych lub pamięci podręcznej Redis). Przed walidacją JWT sprawdź, czy znajduje się on na czarnej liście. `djangorestframework-simplejwt` zapewnia wbudowaną obsługę czarnej listy tokenów odświeżania.
- Krótkie czasy wygaśnięcia: Używaj krótkich czasów wygaśnięcia tokena dostępu i polegaj na tokenach odświeżania, aby często uzyskiwać nowe tokeny dostępu. Ogranicza to okno możliwości wykorzystania naruszonego tokena.
- Obracaj tokenami odświeżania: Obracaj tokenami odświeżania po każdym użyciu. Spowoduje to unieważnienie starych tokenów za każdym razem i zapobiegnie kradzieży tokenów.
OAuth2 i OpenID Connect
W bardziej złożonych scenariuszach uwierzytelniania i autoryzacji rozważ użycie OAuth2 i OpenID Connect. Te standardy zapewniają solidne ramy delegowania dostępu do zasobów bez udostępniania danych uwierzytelniających. OAuth2 to przede wszystkim protokół autoryzacji, a OpenID Connect opiera się na OAuth2, aby świadczyć usługi uwierzytelniania. Kilka pakietów Django, takich jak `django-oauth-toolkit` i `django-allauth`, ułatwia integrację OAuth2 i OpenID Connect z Twoimi API DRF.
Przykładowy scenariusz: Użytkownik chce przyznać aplikacji innej firmy dostęp do swoich danych przechowywanych w Twoim API. Dzięki OAuth2 użytkownik może autoryzować aplikację bez udostępniania swojej nazwy użytkownika i hasła. Zamiast tego aplikacja otrzymuje token dostępu, którego może użyć do uzyskania dostępu do danych użytkownika w zdefiniowanym zakresie uprawnień.
Wybór właściwej metody uwierzytelniania
Najlepsza metoda uwierzytelniania zależy od Twoich konkretnych wymagań:
- Prostota i szybkość implementacji: Uwierzytelnianie Tokenem jest generalnie łatwiejsze do zaimplementowania na początku.
- Skalowalność: Uwierzytelnianie JWT jest bardziej skalowalne dla API o dużym natężeniu ruchu.
- Wymagania bezpieczeństwa: Rozważ wrażliwość swoich danych i wymagany poziom bezpieczeństwa. OAuth2/OpenID Connect oferują najbardziej solidne funkcje bezpieczeństwa, ale wymagają bardziej złożonej implementacji.
- Architektura mikroserwisów: JWT dobrze nadają się do mikroserwisów, ponieważ każda usługa może niezależnie weryfikować tokeny.
Najlepsze praktyki w zakresie uwierzytelniania API
- Używaj HTTPS: Zawsze używaj HTTPS do szyfrowania komunikacji między klientem a serwerem, chroniąc dane uwierzytelniające przed podsłuchem.
- Przechowuj sekrety w bezpieczny sposób: Nigdy nie przechowuj kluczy tajnych ani haseł w postaci zwykłego tekstu. Używaj zmiennych środowiskowych lub bezpiecznych narzędzi do zarządzania konfiguracją.
- Zaimplementuj ograniczanie szybkości: Chroń swoje API przed nadużyciami, implementując ograniczanie szybkości, aby ograniczyć liczbę żądań, które klient może wysłać w danym okresie czasu.
- Sprawdzaj poprawność danych wejściowych: Dokładnie sprawdzaj poprawność wszystkich danych wejściowych, aby zapobiec atakom typu injection.
- Monitoruj i rejestruj: Monitoruj swoje API pod kątem podejrzanej aktywności i rejestruj zdarzenia uwierzytelniania do celów audytowych.
- Regularnie aktualizuj biblioteki: Aktualizuj swoje biblioteki Django, DRF i uwierzytelniania, aby korzystać z poprawek bezpieczeństwa i ulepszeń.
- Zaimplementuj CORS (Cross-Origin Resource Sharing): Poprawnie skonfiguruj CORS, aby umożliwić dostęp do Twojego API z przeglądarek internetowych tylko z zaufanych domen.
Wniosek
Wybór odpowiedniej metody uwierzytelniania ma kluczowe znaczenie dla zabezpieczenia Twoich API DRF. Uwierzytelnianie Tokenem oferuje prostotę, a uwierzytelnianie JWT zapewnia skalowalność i elastyczność. Zrozumienie zalet i wad każdej metody, wraz z najlepszymi praktykami w zakresie bezpieczeństwa API, umożliwi Ci budowanie solidnych i bezpiecznych API, które chronią Twoje dane i użytkowników.
Pamiętaj, aby wziąć pod uwagę swoje specyficzne potrzeby i wybrać rozwiązanie, które najlepiej równoważy bezpieczeństwo, wydajność i łatwość implementacji. Zapoznaj się z OAuth2 i OpenID Connect w przypadku bardziej złożonych scenariuszy autoryzacji.